<template>
  <component
    ref="formEl"
    :is="formRegister ? 'w-form-element' : 'div'"
    v-bind="
      formRegister && { validators, inputValue: rating, disabled, readonly }
    "
    v-model:valid="valid"
    @reset="
      $emit('update:modelValue', (rating = null));
      $emit('input', null);
    "
    :class="classes"
    ><input
      :id="inputName"
      :name="inputName"
      type="hidden"
      :value="rating"/><template v-for="i in max" :key="i">
      <slot v-if="$slots.item" name="item" :index="i + 1"></slot
      ><button
        class="w-rating__button"
        :disabled="disabled || readonly"
        @mouseenter="hover = i"
        @mouseleave="hover = 0"
        @click="onButtonClick(i)"
        @focus="onFocus"
        @blur="onBlur"
        @keydown="onKeydown"
        :class="buttonClasses(i)"
        type="button"
        :tabindex="i === 1 ? 0 : -1"
      >
        <i
          class="w-icon"
          v-if="i - 1 === ~~rating && rating - ~~rating"
          role="icon"
          :class="`${icon} ${color}`"
          aria-hidden="true"
          :style="halfStarStyle"
        ></i>
      </button> </template
  ></component>
</template>

<script>
import { ref, reactive, computed, watch, getCurrentInstance } from "vue";
export default {
  name: "w-rating",
  inject: { formRegister: { default: null } },
  props: {
    modelValue: {},
    max: { type: Number, default: 5 },
    color: { type: String, default: "primary" },
    bgColor: { type: String, default: "grey-light4" },
    icon: { type: String, default: "wi-star" },
    xs: { type: Boolean },
    sm: { type: Boolean },
    md: { type: Boolean },
    lg: { type: Boolean },
    xl: { type: Boolean },
    noRipple: { type: Boolean },
    // Also name, disabled, readonly, required and validators in the mixin.
    name: { type: String }, // When sending data through form.
    disabled: { type: Boolean },
    readonly: { type: Boolean },
    required: { type: Boolean },
    validators: { type: Array }
  },

  emits: ["input", "update:modelValue", "focus", "blur"],
  setup(props, ctx) {
    const instance = getCurrentInstance();
    const rating = ref(parseFloat(props.modelValue || 0));
    const hover = ref(0); // The index (starts at 1) of the currently hovered button.
    const hasFocus = ref(0); // The index (starts at 1) of the currently focused button.
    const ripple = reactive({
      start: false,
      end: false,
      timeout: null
    });
    const valid = ref(null);
    const size = computed(() => {
      return (
        (props.xs && "xs") ||
        (props.sm && "sm") ||
        (props.lg && "lg") ||
        (props.xl && "xl") ||
        "md"
      );
    });
    const classes = computed(() => {
      return {
        "w-rating": true,
        "w-rating--focus": hasFocus.value,
        "w-rating--hover": hover.value,
        "w-rating--disabled": props.disabled,
        "w-rating--readonly": props.readonly,
        "w-rating--ripple": ripple.start,
        "w-rating--rippled": ripple.end
      };
    });

    const halfStarStyle = computed(() => {
      return {
        width:
          hover.value <= ~~rating.value &&
          `${(rating.value - ~~rating.value) * 100}%`
      };
    });

    const inputName = computed(() => {
      return (
        props.name || `${instance.type.name}--${instance.type.uid || instance.uid}`
      );
    });

    function onButtonClick(i) {
      rating.value = i;
      ctx.emit("update:modelValue", rating.value);
      ctx.emit("input", rating.value);

      if (!props.noRipple) {
        ripple.start = true;
        ripple.timeout = setTimeout(() => {
          ripple.start = false;
          ripple.end = true;
          setTimeout(() => (ripple.end = false), 100);
        }, 700);
      }
    }

    function onFocus(e) {
      hasFocus.value = true;
      ctx.emit("focus", e);
    }

    function onBlur(e) {
      hasFocus.value = false;
      ctx.emit("blur", e);
    }

    function onKeydown(e) {
      if ([37, 38, 39, 40].includes(e.keyCode)) {
        if ([39, 40].includes(e.keyCode)) {
          rating.value <= props.max - 1 && rating.value++;
        } else if (rating.value > 1) {
          rating.value--;
        }
        const sibling = instance.ctx.$el.querySelectorAll("button")[
          rating.value - 1
        ];
        if (sibling) {
          sibling.focus();
          sibling.click();
        }

        e.preventDefault();
      }
    }

    function buttonClasses(i) {
      const isHalf = i - 1 === ~~rating.value && rating.value - ~~rating.value;
      const isOn =
        hover.value >= i || (!isHalf && hover.value === 0 && rating.value >= i);
      return {
        "w-rating__button--on": isOn,
        "w-rating__button--half": isHalf,
        [props.icon]: true,
        [`size--${size.value}`]: true,
        [isOn ? props.color : props.bgColor]: true
      };
    }
    // TODO: CHANGE
    // watch(
    //   () => props.modelValue,
    //   value => {
    //     rating.value = value;
    //   }
    // );

    return {
      rating,
      hover,
      hasFocus,
      ripple,
      valid,
      size,
      classes,
      halfStarStyle,
      inputName,
      // methods
      onButtonClick,
      onFocus,
      onBlur,
      onKeydown,
      buttonClasses
    };
  }


  // watch: {
  //   value(value) {
  //     this.rating = value;
  //   }
  // }
};
</script>